前言

这是 wordpress 插件 loginizer 的一个洞,学习了这个 csrf 漏洞的产生过程后,让我对 wordpress 的 hook 机制又有了一点理解 : )

审计

首先从 github 上把 1.3.5 版本的 loginizer 插件下载下来并安装。根据参考文章知道,该 csrf 漏洞可以将 loginizer 插件中设置的黑、白名单中的信息删除。

在该插件中找到对应的操作。

01

可以看到,在删除黑、白名单信息的操作中,并没有使用 wordpress 提供的 check_admin_referer 函数去预防 csrf。这两个操作都在 loginizer_page_brute_force 这个函数中,而在 wordpress 中,是通过 hook 机制去使用插件的,所以该 loginizer_page_brute_force 函数肯定被绑定到了一个 hook 上。

在该文件中搜索 loginizer_page_brute_force。

02

在 673 行,先调用了 add_action 函数将 loginizer_admin_menu 这个函数绑定到了 admin_menu 这个 hook 上,当 loginizer_admin_menu 函数被调用时,其中会调用 add_submenu_page 这个函数,而存在漏洞代码的函数的函数名 loginizer_page_brute_force 被当作参数传入。

我们在该处打个断点,然后访问触发漏洞的文件 /wp-admin/admin.php。

03

跟进 add_submenu_page 函数。

04

可以看到通过 add_action 函数,将 loginizer_page_brute_force 函数绑定到了 loginizer-security_page_loginizer_brute_force 这个 hook 上。

所以,当 loginizer-security_page_loginizer_brute_force 这个 hook 被触发时,就会调用存在漏洞的函数。接下来,我们看一下在哪里会触发这个 hook。

因为在 wordpress 中,会通过 do_action 函数去触发一个 hook,在 /wp-admin/admin.php 中搜索一下do_action(,可以看到传入该函数中的 hook 名字完全可控的地方在 226 行。

1
do_action( $page_hook );

接着,我们回到 wordpress 后台,然后在该插件的设置中添加一个黑名单信息。

05

看一下 delete 操作提交的地址。

06

可以看到该操作将请求提交到了 /wp-admin/admin.php 中,并传入了 page 和 bdelid 两个参数。第一个参数负责找到执行删除操作的函数所绑定的 hook,第二个参数表示要删除黑名单中的哪一条信息。

在 /wp-admin/admin.php 的第 120 行取出了 page 参数,然后经过 wp_unslash 和 plugin_basename 两个函数处理后赋值给了 plugin_page 变量。

1
2
3
4
if ( isset($_GET['page']) ) { // 
$plugin_page = wp_unslash( $_GET['page'] ); // loginizer_brute_force 反转义
$plugin_page = plugin_basename($plugin_page);
}

跟进该变量,在 163 行通过 get_plugin_page_hook 函数取到了一个 hook 值,并将该值赋给了 page_hook 变量。

07

此时,page_hook 中的值就是可以触发漏洞的 hook 了。

我们再看一下删除黑、白名单信息的函数 loginizer_page_brute_force。实际上,在该函数中是调用了 check_admin_referer 函数去防止 csrf 的,只不过只考虑了 post 传参的情况,而删除黑、白名单信息只需要使用 get 请求。

1
2
3
4
// /wp-content/plugins/loginizer/init.php line 1144
if(count($_POST) > 0){
check_admin_referer('loginizer-options');
}

在前面的操作中,我们已经在该插件中添加了一条黑名单信息,接着我们写一个带有 payload 的表单。

08

然后,在登录了后台用户的情况下,提交本地另一虚拟主机中的 payload 表单,即可利用 csrf 漏洞去删除 loginizer 插件中的黑名单信息。

修复

我从官网下载了 loginizer 插件的最新版 1.4.2,和存在漏洞的 1.3.5 进行了对比。

09

在 loginizer 插件的最新版本中,将删除黑、白名单信息的操作都改成了 post 请求,这样就会在删除前先调用 check_admn_referer 函数对请求来源做验证,从而避免了 csrf 漏洞。

ref :
https://lorexxar.cn/2017/10/25/wordpress/#Loginizer-CSRF%E6%BC%8F%E6%B4%9E-CVE-2017-12651